#ifndef __UCACHE__
#define __UCACHE__

#define FUSE_USE_VERSION 30

#include <mstore.h>
#include <util.h>

#include <fuse.h>
#include <fuse/fuse_opt.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

typedef struct fuse_operations UCacheOps;
typedef struct UCache UCache;
typedef struct UCacheArgs UCacheArgs;
typedef struct UCacheObj UCacheObj;
typedef struct UCacheKey UCacheKey;
typedef struct Disk Disk;

#define MEMORY_MAX_DEFAULT	(1024*1024*2)	// 2GB

/*
 * If updated, make sure to change ucache_args and fuse_ops
 * in ucache.c
 */
struct UCacheArgs
{
	const char*	mountpoint;
	u64		memrange;
	u64		nodesz;
	bool		verbose;
};

#define UCACHE_OPT(t, p, v) { t, offsetof(UCacheArgs, p), v }

enum
{
	CACHE_ENOMEM,
	CACHE_EREGISTERED,
};

struct UCache
{
	UCacheArgs*		args;		// UCache Arguments
	MStore*			mstore;		// MStore instance
	Disk*			disk;		// Primary disk
	u64			mem_max;	// Maximum memory allowed to be allocated
	u64			mem_alloced;	// Current memory allocated
};

enum
{
	UOBJ_BLANK,	// Allocated, but not initialized
	UOBJ_FILE,	// File type
	UOBJ_DENTRY,	// Directory Entry Type
};

struct UCacheObj
{
        u64             priority;	// Cache priority
	u64		inum;		// INode number (ucache)
	u8		type;		// Object Type
	u64		bufsz;		// Buffer size

	/* File Attributes */
	uid_t		uid;		// User ID of owner
	gid_t		gid;		// Group ID of owner
	off_t		size;		// Size in bytes
	time_t		atime;		// Time of last access
	time_t		mtime;		// Time of last modification
	time_t		ctime;		// Time of last status change

        union {
                u8*     	cbuf;
                void*   	vbuf;
        } co_ref;
};

/*
 * To avoid getting too integrated with a single databse
 * we are going to have a wrapping API for the cache database.
 * Each method associates an operation done to the UCache instance
 * provided.
 */

enum
{
	UMDB_EOPEN,	// Already open
	UMDB_ECLOSED,	// DB is closed
	UMDB_ERR,	// DB Error
	UMDB_ENOIMP,	// Unimplemented
};

/*
 * UCache Metadata Database API (Wrappers)
 */
extern int umdb_open(UCache* uc, Error *err);
extern int umdb_close(UCache* uc, Error *err);
extern int umdb_put(UCacheObj* uobj, UCache* uc, Error *err);
extern int umdb_get(MHash key, UCacheObj **uobj, UCache *uc, Error *err);
extern int umdb_delete(UCacheObj* uobj, UCache* uc, Error *err);

extern UCache*		ucache_init(Error *err);

extern UCacheObj*	allocobj(u64 bufsz, UCache* uc);
extern UCache*		getinstance();

/*
 * On-Disk Operations
 */
int disk_open(const char *path, Disk **indisk, Error *err);
int disk_write(UCacheObj *obj, Disk *disk, Error *err);

/*
 * UCache FUSE Operations
 */
extern int	fuse_fgetattr(const char* path, struct stat* statbuf);
extern int	fuse_freaddir(const char* path, void* buffer, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* finfo);
extern int	fuse_create(const char* path, mode_t mode, struct fuse_file_info* info);
/*
 * Utility methods
 */
extern u64 hashpath(const char* path);

#endif
